// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

%include {
	#include "query.h"
	#include "parser.h"
	#include "traits.h"

	static ASTNode* recursiveNegate(ASTNode* node);
	static uint reverseComparison(uint op);
}
%token_prefix TK_
%token_type {const char*}
%token_destructor { if ($$) delete [] $$; }
%default_type {ASTNode*}
%default_destructor { if ($$) delete $$; }
%extra_argument {QueryCompiler* compiler}

%left AND.
%left OR.
%nonassoc EQ NE LE LT GE GT.
%right NOT.


%parse_failure {
	compiler->fail("Syntax error");	
}

%syntax_error {
	compiler->fail("Syntax error");	
}

compilationUnit ::= query(X). {
	compiler->success(X);
}

query(Z) ::= select(X) where(Y) orderBy(W). {
	Z = new ASTNode(ASTNode::QUERY);
	if (X != 0) {
		Z->children.push_back(X);
	}
	if (Y != 0) {
		Z->children.push_back(Y);
	}
	if (W != 0) {
		Z->children.push_back(W);
	}
}

select(Z) ::= SELECT TOP integerValue(X) nonbooleanExpression(Y). {
	Z = new ASTNode(ASTNode::SELECT, new ASTNode(ASTNode::TOP, X), Y);
}

select(Z) ::= SELECT nonbooleanExpression(X). {
	Z = new ASTNode(ASTNode::SELECT, X);
}

select(Z) ::= select(W) COMMA nonbooleanExpression(X). {
	Z = W;
	Z->children.push_back(X); 
}

where(Z) ::= . {
	Z = 0;
}

where(Z) ::= WHERE booleanExpression(X). {
	Z = new ASTNode(ASTNode::WHERE, X);
}


orderBy(Z) ::= . {
	Z = 0;
}

orderBy(Z) ::= orderBySequence(X). {
	Z = X;
}

orderBySequence(Z) ::= ORDER BY nonbooleanExpression(X) ascOrDesc(Y). {
	Z = new ASTNode(ASTNode::ORDER_BY, new ASTNode(Y ? ASTNode::ASC : ASTNode::DESC, X));
}

orderBySequence(Z) ::= orderBySequence(W) COMMA nonbooleanExpression(X) ascOrDesc(Y). {
	Z = W;
	Z->children.push_back(new ASTNode(Y ? ASTNode::ASC : ASTNode::DESC, X)); 
} 

%type ascOrDesc {bool}
%destructor ascOrDesc {}

ascOrDesc(Z) ::= . {
	Z = true; 
}

ascOrDesc(Z) ::= ASC. {
	Z = true; 
}

ascOrDesc(Z) ::= DESC. {
	Z = false; 
}




booleanExpression(Z) ::= NOT booleanExpression(X). {
	Z = recursiveNegate(X);
}

booleanExpression(Z) ::= booleanExpression(X) OR booleanExpression(Y). {
	if (X->type == ASTNode::OR) {
		Z = X;
		if (Y->type == ASTNode::OR) {
			Z->children.insert(Z->children.begin(), Y->children.begin(), Y->children.end());
			Y->children.clear();
			delete Y;
		} else {
			Z->children.push_back(Y);
		}
	} else if (Y->type == ASTNode::OR) {
		Z = Y;
		Z->children.insert(Z->children.begin(), X);
	} else {
		Z = new ASTNode(ASTNode::OR, X, Y);
	}
}

booleanExpression(Z) ::= booleanExpression(X) AND booleanExpression(Y). {
	if (X->type == ASTNode::AND) {
		Z = X;
		if (Y->type == ASTNode::AND) {
			Z->children.insert(Z->children.begin(), Y->children.begin(), Y->children.end());
			Y->children.clear();
			delete Y;
		} else {
			Z->children.push_back(Y);
		}
	} else if (Y->type == ASTNode::AND) {
		Z = Y;
		Z->children.insert(Z->children.begin(), X);
	} else {
		Z = new ASTNode(ASTNode::AND, X, Y);
	}
}

booleanExpression(Z) ::= LPAR booleanExpression(X) RPAR. {
	Z = X;
}

booleanExpression(Z) ::= stringExpression(X) comparison(OP) stringValue(Y). { 
	Z = new ASTNode(OP, X, Y);
}

booleanExpression(Z) ::= decimalExpression(X) comparison(OP) decimalValue(Y). {
	Z = new ASTNode(OP, X, Y);
}

booleanExpression(Z) ::= integerExpression(X) comparison(OP) integerValue(Y). {
	Z = new ASTNode(OP, X, Y);
}

booleanExpression(Z) ::= stringValue(X) comparison(OP) stringExpression(Y). { 
	Z = new ASTNode(reverseComparison(OP), Y, X);
}

booleanExpression(Z) ::= decimalValue(X) comparison(OP) decimalExpression(Y). {
	Z = new ASTNode(reverseComparison(OP), Y, X);
}

booleanExpression(Z) ::= integerValue(X) comparison(OP) integerExpression(Y). {
	Z = new ASTNode(reverseComparison(OP), Y, X);
}

booleanExpression(Z) ::= stringExpression(X) comparison(OP) NULL. {
	uint8 buffer[8];	
	Z = new ASTNode(OP, X, new ASTNode(ASTNode::STRING_VALUE, buffer, StringTraits::encodeNull(buffer)));
}

booleanExpression(Z) ::= integerExpression(X) comparison(OP) NULL. {
	uint8 buffer[8];
	Z = new ASTNode(OP, X, new ASTNode(ASTNode::INTEGER_VALUE, buffer, IntegerTraits::encodeNull(buffer)));
}

booleanExpression(Z) ::= decimalExpression(X) comparison(OP) NULL. {
	uint8 buffer[8];
	Z = new ASTNode(OP, X, new ASTNode(ASTNode::DECIMAL_VALUE, buffer, DecimalTraits::encodeNull(buffer)));
}

%type comparison {uint}
%destructor comparison {}

comparison(Z) ::= EQ. { Z = ASTNode::EQ; }
comparison(Z) ::= LE. { Z = ASTNode::LE; }
comparison(Z) ::= LT. { Z = ASTNode::LT; }
comparison(Z) ::= GE. { Z = ASTNode::GE; }
comparison(Z) ::= GT. { Z = ASTNode::GT; }
comparison(Z) ::= NE. { Z = ASTNode::NE; }

nonbooleanExpression(Z) ::= stringExpression(X). {
    Z = X;
}

nonbooleanExpression(Z) ::= decimalExpression(X). {
    Z = X;
}

nonbooleanExpression(Z) ::= integerExpression(X). {
    Z = X;
}


stringExpression(Z) ::= LPAR stringExpression(X) RPAR. {
	Z = X;
}

decimalExpression(Z) ::= LPAR decimalExpression(X) RPAR. {
	Z = X;
}

integerExpression(Z) ::= LPAR integerExpression(X) RPAR. {
	Z = X;
}

stringExpression(Z) ::= STRING_ID(X). {
	Z = new ASTNode(ASTNode::STRING_ID, X, (uint) strlen(X));
	delete [] X;
}

decimalExpression(Z) ::= DECIMAL_ID(X). {
	Z = new ASTNode(ASTNode::DECIMAL_ID, X, (uint) strlen(X));
	delete [] X;
}

integerExpression(Z) ::= INTEGER_ID(X). {
	Z = new ASTNode(ASTNode::INTEGER_ID, X, (uint) strlen(X)); 
	delete [] X;
}

stringValue(Z) ::= STRING_LITERAL(X). { 
	Z = new ASTNode(ASTNode::STRING_VALUE, X, (uint) strlen(X)); 
	delete [] X;
}

decimalValue(Z) ::= DECIMAL_LITERAL(X). {
	Z = new ASTNode(ASTNode::DECIMAL_VALUE, X, DecimalTraits::sizeOf((const uint8*) X)); 
	delete [] X;
}

integerValue(Z) ::= INTEGER_LITERAL(X). {
	Z = new ASTNode(ASTNode::INTEGER_VALUE, X, IntegerTraits::sizeOf((const uint8*) X)); 
	delete [] X;
}

%code {

static void* cppMalloc(size_t size) {
	return new char[size];
}

static void cppFree(void* ptr) {
	delete [] (char*) ptr;
}

void QueryCompiler::begin() {
	failed = false;
	parser = ParseAlloc(cppMalloc);	
}

void QueryCompiler::end() {
	ParseFree(parser, cppFree);
	parser = 0;	
}

void QueryCompiler::push(int token, const void* value) {
	Parse(parser, token, (const char*) value, this);
}


static ASTNode* recursiveNegate(ASTNode* node) {
	if (node->type == ASTNode::AND || node->type == ASTNode::OR) {
		node->type = (node->type == ASTNode::AND? ASTNode::OR : ASTNode::AND);
		for (uint i = 0; i < node->children.size(); i++) {
			recursiveNegate(node->children[i]);
		}
	} else if (ASTNode::EQ <= node->type && node->type <= ASTNode::NE) {
		node->type = ASTNode::NE - (node->type - ASTNode::EQ);
	}
	return node;
}

static uint reverseComparison(uint op) {
	switch (op) {
		case ASTNode::EQ: return ASTNode::EQ;
		case ASTNode::LE: return ASTNode::GE;
		case ASTNode::LT: return ASTNode::GT;
		case ASTNode::GE: return ASTNode::LE;
		case ASTNode::GT: return ASTNode::LT;
		case ASTNode::NE: return ASTNode::NE;
	}
	return op;
}

}
